package com.qzuser.sendmessagebasic.client;

import com.qzuser.sendmessagebasic.client.req.Request;
import com.qzuser.sendmessagebasic.client.resp.RespPayload;
import com.qzuser.sendmessagebasic.client.resp.Response;
import com.qzuser.sendmessagebasic.client.resp.SubscribeEntity;
import com.qzuser.sendmessagebasic.common.exception.*;
import com.qzuser.sendmessagebasic.common.tool.BinaryTool;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Connection {
    private Socket socket;
    private final ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();
    private InputStream inputStream;
    private OutputStream outputStream;
    private DataInputStream in;
    private DataOutputStream out;
    private ExecutorService callbackPool;

    public Channel createChannel() {
        String channelId = "C-" + UUID.randomUUID();
        Channel channel = new Channel(channelId, this);
        channelMap.put(channelId, channel);
        boolean ok = channel.createChannel();
        if (!ok) {
            channelMap.remove(channelId);
            return null;
        }
        return channel;
    }

    public Connection(String host, int port) {
        try {
            socket = new Socket(host, port);
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
            in = new DataInputStream(inputStream);
            out = new DataOutputStream(outputStream);
            callbackPool = Executors.newFixedThreadPool(4);
            new Thread(() -> {
                try {
                    while (!socket.isClosed()) {
                        Response response = readResponse();
                        dispatch(response);
                    }
                } catch (SocketException e) {
                    System.out.println("[Connection] 连接正常关闭");
                } catch (IOException e) {
                    System.out.println("[Connection] 连接异常关闭");
                    e.printStackTrace();
                }
            }).start();
        } catch (IOException e) {
            System.out.println("[Connection] 连接异常关闭");
            e.printStackTrace();
        }
    }

    private Response readResponse() throws IOException {
        Response response = new Response();
        response.setType(in.readInt());
        response.setLength(in.readInt());
        byte[] payload = new byte[response.getLength()];
        in.readFully(payload);
        response.setPayload(payload);
        return response;
    }

    private void dispatch(Response response) {
        try {
            if (response.getType() == 0xc) {
                SubscribeEntity subscribe = (SubscribeEntity) BinaryTool.fromByteArray(response.getPayload());
                Channel channel = channelMap.get(subscribe.getChannelId());
                if (channel == null) {
                    throw new ConnectionException("[Connection] channel不存在：" + subscribe.getChannelId() + "，无法通信");
                }
                callbackPool.submit(() -> {
                    try {
                        channel.getConsumer().handleDelivery(subscribe.getConsumerTag(), subscribe.getProperties(), subscribe.getBody());
                    } catch (ConsumeException | ToolException | IOException | BrokerException e) {
                        e.printStackTrace();
                    }
                });
            } else {
                RespPayload respPayload = (RespPayload) BinaryTool.fromByteArray(response.getPayload());
                Channel channel = channelMap.get(respPayload.getChannelId());
                if (channel == null) {
                    throw new ConnectionException("[Connection] channel不存在：" + respPayload.getChannelId() + "，无法通信");
                }
                channel.putRespPayload(respPayload);
            }
        } catch (ToolException | ConnectionException e) {
            e.printStackTrace();
        }
    }

    public void writeRequest(Request request) {
        try {
            out.writeInt(request.getType());
            out.writeInt(request.getLength());
            out.write(request.getPayload());
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            callbackPool.shutdownNow();
            channelMap.clear();
            inputStream.close();
            outputStream.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
